โ– humdrum codex / glint v1.0.2
license AGPL-3.0
7.2 KB raw
id
TASK-021
title
PDF / printable export in the house style
status
๐Ÿ Done
assignee
created_date
2026-06-29 20:49
updated_date
2026-06-30 04:01
labels
feature, release-1
dependencies
priority
medium
ordinal
21000

Description

Add a glint command/keybind to export the current document as a clean, printable PDF (or print-ready HTML) in the AppKit house style โ€” matching what _shared-app-kit/markdown-doc-kit already produces for the browser.

Reuse that kit as the source of truth rather than reinventing styling: it has doc.css (tokens, the seven themes โ€” flexoki/-dark, uchu/-dark, humdrum/-dark, eink โ€” document typography, and the @media print rules), embedded fonts (Awke, Untitled Sans, Name Mono), and pagination conventions (opening '# Title' becomes a centered cover page; every '## Section' starts a new page; '{.page-break}' forces a break; US Letter, 1in margins; print collapses to black-on-white).

Approach options to decide during design:

Out of scope: reimplementing the kit's interactive features (checkbox toggle, drag reorder) in glint.

Acceptance Criteria

Implementation Plan

  1. Vendor kit assets into internal/export/assets/: doc.css (neutralize the 3 --font-* :root tokens to open/system fallbacks, strip nothing else โ€” no @font-face present) + sync.sh that re-copies from _shared-app-kit and re-neutralizes. go:embed them.
  2. config: add font_display/font_body/font_mono keys (Config fields + Default + loadFromFile overlay), defaults = Georgia serif / system-ui sans / ui-monospace.
  3. internal/export package (TDD): Document(md, Options)->html pure fn โ€” goldmark GFM render, strip YAML frontmatter, postprocess (cover-wrap leading h1+subtitle p, task-list-item class, {.page-break} heading suffix->class), inject :root font-token override from Options, wrap in article.doc + embedded doc.css. MapTheme(glintTheme)->kit data-theme. ToFile writes .html next to source. OpenInBrowser (darwin open / linux xdg-open / win start).
  4. Wire Ctrl+E in app.handleKey -> export current buffer, write html next to file, open browser, set status. Graceful: if no path (unnamed) save-as first or export to temp.
  5. README + help overlay: document Ctrl+E export, the browser Print->Save-as-PDF path, and the 3 font config keys.
  6. Tests: Document output contains doc.css, cover div, page-break class, configured fonts, mapped theme; frontmatter stripped.

Implementation Notes

Portability constraint (user): glint is installed by people who are NOT on this machine and do NOT have the kit's bundled fonts (Awke, Untitled Sans, Name Mono โ€” personal/licensed). So the export must NOT hard-embed those. Fonts must be user-configurable (display/body/mono config keys, reusing the doc.css --font-* tokens) with safe open/system fallbacks (e.g. Georgia / system-ui / ui-monospace) so output looks fine with zero personal assets. Only redistributable fonts may be embedded by default.

Packaging: glint must be self-contained. Copy/build the required kit pieces (doc.css + the @media print rules, the render HTML skeleton, only redistributable fonts) into the glint repo (e.g. internal/export/assets/) and embed them with go:embed so they ship inside the binary. Do NOT depend on /Users/kortum/Developer/Home/_shared-app-kit/markdown-doc-kit at runtime โ€” that path doesn't exist on users' machines. Decide a vendor/sync story (a script that re-copies from the kit on update, or a one-time fork) so the embedded copy can be refreshed when the kit's house style changes; strip the licensed Awke/Untitled Sans/Name Mono @font-face blocks during that copy, leaving the --font-* tokens + fallbacks (ties to the configurable-fonts AC).

Implemented: internal/export package (TDD, 17 tests). Document() renders md via goldmark GFM, strips YAML frontmatter, postprocesses (cover-wrap leading h1+subtitle, task-list-item class, {.page-break} heading suffix->class), embeds vendored doc.css (go:embed) + injects configured --font-* tokens. MapTheme glint->kit. Write()/OutputPath()/Title()/OpenInBrowser() for file+browser. Ctrl+E wired in app.exportPDF (writes .html next to file, opens browser, graceful path-print fallback if open fails). config: font_display/body/mono keys w/ portable defaults (Georgia/system-ui/ui-monospace); wizard preserves them. assets/sync.sh re-vendors doc.css from kit + strips licensed faces (Awke/Untitled Sans/Name Mono). README Export section + help overlay updated. Fixed bug found in real output: heading {.class} regex used (?s) dotall and leaked page-break across to an earlier heading w/ mismatched close tag; removed dotall.

Follow-up fix: WebKit (Safari/Orion) blocks locally-installed user fonts (~/Library/Fonts) from web content, so custom configured fonts rendered as default serif. Added font embedding (internal/export/fonts.go): scans user font dirs, parses sfnt name tables (golang.org/x/image/font/sfnt), inlines matching faces as base64 @font-face (bounded to regular+bold x roman+italic, ~3MB). Best-effort + portable: unfound fonts fall back to name reference; binary still bundles nothing licensed. Also quote multi-word family names in the CSS override (WebKit var() quirk). Default fonts (Georgia/system-ui/ui-monospace) are WebKit-allowed so default exports were unaffected.